﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace SpellOutCheckAmount
{
    [SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Reviewed. Suppression is OK here.")]
    public class CheckAmount
    {
        private static readonly Dictionary<decimal, string> ones;
        private static readonly Dictionary<decimal, string> tens;

        private readonly decimal amount;

        static CheckAmount()
        {
            ones = new Dictionary<decimal, string>
                   {
                       { 0, "Zero" },
                       { 1, "One" },
                       { 2, "Two" },
                       { 3, "Three" },
                       { 4, "Four" },
                       { 5, "Five" },
                       { 6, "Six" },
                       { 7, "Seven" },
                       { 8, "Eight" },
                       { 9, "Nine" },
                       { 10, "Ten" },
                       { 11, "Eleven" },
                       { 12, "Twelve" },
                       { 13, "Thirteen" },
                       { 14, "Fourteen" },
                       { 15, "Fifteen" },
                       { 16, "Sixteen" },
                       { 17, "Seventeen" },
                       { 18, "Eighteen" },
                       { 19, "Nineteen" }
                   };

            tens = new Dictionary<decimal, string>
                   {
                       { 2, "Twenty" },
                       { 3, "Thirty" },
                       { 4, "Forty" },
                       { 5, "Fifty" },
                       { 6, "Sixty" },
                       { 7, "Seventy" },
                       { 8, "Eighty" },
                       { 9, "Ninety" }
                   };
        }

        public CheckAmount(decimal amount)
        {
            this.amount = amount;
        }

        public override string ToString()
        {
            if (amount < 0 || amount >= 10000)
            {
                throw new NotSupportedException();
            }

            var dollars = decimal.Truncate(amount);
            var cents = (amount - dollars) * 100;

            string centsText = GetCentsText(cents);
            string dollarsText = GetDollarText((int)dollars);

            return dollarsText + centsText + " dollars";
        }

        private static string GetCentsText(decimal cents)
        {
            return cents != 0 ? string.Format(" and {0:00}/100", cents) : string.Empty;
        }

        private static string GetLessThenHudredText(int dollars)
        {
            if (dollars < 20)
            {
                return ones[dollars];
            }

            var unit = dollars % 10;
            var ten = dollars / 10;

            return unit == 0 ? tens[ten] : tens[ten] + "-" + ones[unit].ToLower();
        }

        private string GetDollarText(int dollars)
        {
            if (dollars < 100)
            {
                return GetLessThenHudredText(dollars % 100);
            }

            return dollars < 1000 ? GetHundredText(dollars) : GetThousandText(dollars);
        }

        private string GetThousandText(int dollars)
        {
            var thousands = dollars / 1000;
            var lessThanOneThousand = dollars % 1000;

            return lessThanOneThousand == 0
                       ? ones[thousands] + " thousand"
                       : ones[thousands] + " thousand " + this.GetHundredText(lessThanOneThousand).ToLower();
        }

        private string GetHundredText(int dollars)
        {
            var hundreds = dollars / 100;
            var lessThenHundredDollars = dollars % 100;

            if (lessThenHundredDollars == 0)
            {
                return ones[hundreds] + " hundred";
            }

            return this.amount > 1000 && hundreds == 0
                       ? GetLessThenHudredText(lessThenHundredDollars).ToLower()
                       : ones[hundreds] + " hundred " + GetLessThenHudredText(lessThenHundredDollars).ToLower();
        }
    }
}